import type { ConfigurableWindow } from '@vueuse/core';
import type { Pausable } from '@vueuse/shared';
import type { MaybeRefOrGetter } from 'vue';
import { defaultWindow } from '@vueuse/core';
import { tryOnScopeDispose } from '@vueuse/shared';
import { computed, readonly, shallowRef, toValue } from 'vue';
import { isH5 } from '@/utils/platform.utils';

export interface UseRafFnCallbackArguments {
  delta: number
  timestamp: DOMHighResTimeStamp
}

export interface UseRafFnOptions extends ConfigurableWindow {
  immediate?: boolean
  fpsLimit?: MaybeRefOrGetter<number>
  once?: boolean
}

export function useRafFn(
  fn: (args: UseRafFnCallbackArguments) => void,
  options: UseRafFnOptions = {}
): Pausable {
  const {
    immediate = true,
    fpsLimit = undefined,
    window = defaultWindow,
    once = false
  } = options;

  const isActive = shallowRef(false);
  const intervalLimit = computed(() => fpsLimit ? 1000 / toValue(fpsLimit) : 16); // 默认 60fps

  let previousFrameTimestamp = 0;
  let timerId: number | null = null;

  // 判断是否支持 rAF
  const useRaf = isH5 && !!window?.requestAnimationFrame;

  function loop(timestamp: number) {
    if (!isActive.value)
      return;

    if (!previousFrameTimestamp)
      previousFrameTimestamp = timestamp;

    const delta = timestamp - previousFrameTimestamp;

    if (intervalLimit.value && delta < intervalLimit.value - 1) {
      scheduleNext();
      return;
    }

    previousFrameTimestamp = timestamp;
    fn({ delta, timestamp });

    if (once) {
      pause();
      return;
    }

    scheduleNext();
  }

  function scheduleNext() {
    if (!isActive.value)
      return;

    if (useRaf) {
      timerId = window!.requestAnimationFrame(loop);
    }
    else {
      // setTimeout 替代 rAF 帧驱动，带修正机制
      const expectedDelay = intervalLimit.value!;
      timerId = setTimeout(() => {
        const now = Date.now();
        const correctedNow = previousFrameTimestamp + expectedDelay;
        const drift = now - correctedNow;

        // 模拟帧时间点
        loop(correctedNow + drift / 2); // 补偿偏差
      }, expectedDelay) as unknown as number;
    }
  }

  function resume() {
    if (isActive.value)
      return;

    isActive.value = true;
    previousFrameTimestamp = 0;
    scheduleNext();
  }

  function pause() {
    isActive.value = false;
    if (useRaf && timerId != null) {
      window?.cancelAnimationFrame(timerId);
    }
    else if (timerId != null) {
      clearTimeout(timerId);
    }
    timerId = null;
  }

  if (immediate)
    resume();

  tryOnScopeDispose(pause);

  return {
    isActive: readonly(isActive),
    pause,
    resume
  };
}
